#include <stdx/all.h>

const char* GetSourceTemplate()
{
	static u_char data[] = {
		0x23,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x20,0x3C,0x77,0x65,0x62,0x78,0x2F,0x72,
		0x6F,0x75,0x74,0x65,0x2E,0x68,0x3E,0x0A,0x0A,0x3C,0x47,0x4C,0x4F,0x42,0x41,0x4C,
		0x5F,0x53,0x4F,0x55,0x52,0x43,0x45,0x3E,0x0A,0x0A,0x63,0x6C,0x61,0x73,0x73,0x20,
		0x3C,0x54,0x41,0x52,0x47,0x45,0x54,0x5F,0x4E,0x41,0x4D,0x45,0x3E,0x20,0x3A,0x20,
		0x70,0x75,0x62,0x6C,0x69,0x63,0x20,0x77,0x65,0x62,0x78,0x3A,0x3A,0x50,0x72,0x6F,
		0x63,0x65,0x73,0x73,0x42,0x61,0x73,0x65,0x0A,0x7B,0x0A,0x70,0x72,0x6F,0x74,0x65,
		0x63,0x74,0x65,0x64,0x3A,0x0A,0x09,0x69,0x6E,0x74,0x20,0x70,0x72,0x6F,0x63,0x65,
		0x73,0x73,0x28,0x29,0x3B,0x0A,0x7D,0x3B,0x0A,0x0A,0x44,0x45,0x46,0x49,0x4E,0x45,
		0x5F,0x48,0x54,0x54,0x50,0x5F,0x43,0x47,0x49,0x5F,0x45,0x58,0x50,0x4F,0x52,0x54,
		0x5F,0x46,0x55,0x4E,0x43,0x28,0x3C,0x54,0x41,0x52,0x47,0x45,0x54,0x5F,0x4E,0x41,
		0x4D,0x45,0x3E,0x29,0x0A,0x0A,0x69,0x6E,0x74,0x20,0x3C,0x54,0x41,0x52,0x47,0x45,
		0x54,0x5F,0x4E,0x41,0x4D,0x45,0x3E,0x3A,0x3A,0x70,0x72,0x6F,0x63,0x65,0x73,0x73,
		0x28,0x29,0x0A,0x7B,0x0A,0x09,0x3C,0x43,0x47,0x49,0x5F,0x53,0x4F,0x55,0x52,0x43,
		0x45,0x3E,0x0A,0x0A,0x09,0x72,0x65,0x74,0x75,0x72,0x6E,0x20,0x58,0x47,0x5F,0x4F,
		0x4B,0x3B,0x0A,0x7D,0x00
	};

	return (char*)(data);
}

const char* GetMakefileTemplate()
{
	static u_char data[] = {
		0x74,0x61,0x72,0x67,0x65,0x74,0x3A,0x20,0x64,0x6C,0x6C,0x0A,0x0A,0x69,0x6E,0x63,
		0x6C,0x75,0x64,0x65,0x20,0x24,0x28,0x53,0x4F,0x55,0x52,0x43,0x45,0x5F,0x48,0x4F,
		0x4D,0x45,0x29,0x2F,0x6D,0x61,0x6B,0x65,0x63,0x6F,0x6E,0x66,0x0A,0x0A,0x4F,0x42,
		0x4A,0x5F,0x50,0x41,0x54,0x48,0x09,0x3D,0x20,0x2E,0x0A,0x4C,0x49,0x42,0x5F,0x4F,
		0x55,0x54,0x50,0x55,0x54,0x09,0x3D,0x20,0x2E,0x2E,0x2F,0x2E,0x2E,0x2F,0x62,0x69,
		0x6E,0x2F,0x3C,0x54,0x41,0x52,0x47,0x45,0x54,0x5F,0x4E,0x41,0x4D,0x45,0x3E,0x2E,
		0x73,0x6F,0x0A,0x0A,0x4C,0x49,0x42,0x4C,0x49,0x4E,0x4B,0x09,0x3D,0x20,0x3C,0x4C,
		0x49,0x42,0x52,0x41,0x52,0x59,0x3E,0x20,0x2D,0x4C,0x24,0x28,0x4C,0x49,0x42,0x5F,
		0x4F,0x55,0x54,0x50,0x55,0x54,0x5F,0x50,0x41,0x54,0x48,0x29,0x20,0x2D,0x6C,0x77,
		0x65,0x62,0x78,0x20,0x2D,0x6C,0x68,0x74,0x74,0x70,0x20,0x2D,0x6C,0x77,0x65,0x62,
		0x2E,0x64,0x62,0x65,0x6E,0x74,0x69,0x74,0x79,0x20,0x2D,0x6C,0x64,0x62,0x78,0x2E,
		0x62,0x61,0x73,0x65,0x20,0x2D,0x6C,0x7A,0x6C,0x69,0x62,0x20,0x2D,0x6C,0x73,0x74,
		0x64,0x78,0x20,0x2D,0x6C,0x63,0x6C,0x69,0x62,0x20,0x2D,0x6C,0x6A,0x73,0x6F,0x6E,
		0x20,0x24,0x28,0x50,0x4C,0x41,0x54,0x46,0x4F,0x52,0x4D,0x4C,0x49,0x4E,0x4B,0x29,
		0x0A,0x49,0x4E,0x43,0x50,0x41,0x54,0x48,0x09,0x3D,0x20,0x2D,0x49,0x24,0x28,0x4C,
		0x49,0x42,0x52,0x41,0x52,0x59,0x5F,0x50,0x41,0x54,0x48,0x29,0x20,0x2D,0x49,0x2E,
		0x20,0x2D,0x49,0x2E,0x2F,0x69,0x6E,0x63,0x20,0x3C,0x49,0x4E,0x43,0x50,0x41,0x54,
		0x48,0x3E,0x20,0x2D,0x49,0x24,0x28,0x53,0x4F,0x55,0x52,0x43,0x45,0x5F,0x48,0x4F,
		0x4D,0x45,0x29,0x2F,0x77,0x65,0x62,0x61,0x70,0x70,0x2F,0x63,0x70,0x70,0x2F,0x6C,
		0x69,0x62,0x0A,0x0A,0x24,0x28,0x4F,0x42,0x4A,0x5F,0x50,0x41,0x54,0x48,0x29,0x2F,
		0x25,0x2E,0x6F,0x3A,0x25,0x2E,0x63,0x0A,0x09,0x24,0x28,0x43,0x50,0x50,0x29,0x20,
		0x2D,0x44,0x58,0x47,0x5F,0x44,0x4C,0x4C,0x20,0x24,0x28,0x49,0x4E,0x43,0x50,0x41,
		0x54,0x48,0x29,0x20,0x2D,0x63,0x20,0x2D,0x6F,0x20,0x24,0x40,0x20,0x24,0x3C,0x0A,
		0x0A,0x24,0x28,0x4F,0x42,0x4A,0x5F,0x50,0x41,0x54,0x48,0x29,0x2F,0x25,0x2E,0x6F,
		0x3A,0x25,0x2E,0x63,0x70,0x70,0x0A,0x09,0x24,0x28,0x43,0x50,0x50,0x29,0x20,0x2D,
		0x44,0x58,0x47,0x5F,0x44,0x4C,0x4C,0x20,0x24,0x28,0x49,0x4E,0x43,0x50,0x41,0x54,
		0x48,0x29,0x20,0x2D,0x63,0x20,0x2D,0x6F,0x20,0x24,0x40,0x20,0x24,0x3C,0x0A,0x0A,
		0x64,0x6C,0x6C,0x3A,0x20,0x3C,0x54,0x41,0x52,0x47,0x45,0x54,0x5F,0x4E,0x41,0x4D,
		0x45,0x3E,0x2E,0x6F,0x0A,0x09,0x24,0x28,0x43,0x50,0x50,0x5F,0x44,0x4C,0x4C,0x29,
		0x20,0x2D,0x6F,0x20,0x24,0x28,0x4C,0x49,0x42,0x5F,0x4F,0x55,0x54,0x50,0x55,0x54,
		0x29,0x20,0x24,0x28,0x4F,0x42,0x4A,0x5F,0x50,0x41,0x54,0x48,0x29,0x2F,0x3C,0x54,
		0x41,0x52,0x47,0x45,0x54,0x5F,0x4E,0x41,0x4D,0x45,0x3E,0x2E,0x6F,0x20,0x24,0x28,
		0x4C,0x49,0x42,0x4C,0x49,0x4E,0x4B,0x29,0x0A,0x0A,0x63,0x6C,0x65,0x61,0x6E,0x3A,
		0x0A,0x09,0x40,0x24,0x28,0x44,0x45,0x4C,0x45,0x54,0x45,0x46,0x49,0x4C,0x45,0x29,
		0x20,0x24,0x28,0x4F,0x42,0x4A,0x5F,0x50,0x41,0x54,0x48,0x29,0x2F,0x2A,0x2E,0x6F,
		0x00
	};

	return (char*)(data);
}

class HttpCGIEntity
{
protected:
	string name;
	string srcstr;
	string makstr;
	string cgipath;
	string content;

	string translate(const string& str) const
	{
		string content;

		content = stdx::replace(str, "\\", "\\\\");
		content = stdx::replace(content, "\n", "\\n");
		content = stdx::replace(content, "\r", "\\r");
		content = stdx::replace(content, "\t", "\\t");
		content = stdx::replace(content, "\'", "\\\'");
		content = stdx::replace(content, "\"", "\\\"");

		return content;
	}

public:
	const char* getName() const
	{
		return name.c_str();
	}
	bool init(const string& filepath, const string& cgipath)
	{
		if (cgipath.length() > 0 || this->cgipath.empty())
		{
			string str;
			string path = cgipath + "/";

			path = stdx::replace(path, "\\", "/");
			path = stdx::replace(path, "//", "/");

			if (Process::GetCmdParam("-c") == NULL)
			{
				str = path + "cgi.mak";

				if (stdx::GetFileContent(makstr, str) <= 0)
				{
					makstr = GetMakefileTemplate();
				}
			}
			
			str = path + "cgi.cpp";
			
			if (stdx::GetFileContent(srcstr, str) <= 0)
			{
				srcstr = GetSourceTemplate();
				
				if (makstr.empty())
				{
					srcstr = stdx::replace(srcstr, "\nDEFINE_HTTP_CGI_EXPORT_FUNC(<TARGET_NAME>)\n", "");
				}
			}

			this->cgipath = path;
		}

		name = path::name(filepath);
		name = name.substr(0, name.rfind('.'));

		CHECK_FALSE_RETURN(stdx::GetFileContent(content, filepath) > 0);
		
		content = stdx::replace(content, "$filename", name);
		content = stdx::replace(content, "${filename}", name);
		content = stdx::replace(content, "$(filename)", name);

		return true;
	}
	bool create() const
	{
		StringCreator out;
		StringCreator hdrstr;
		StringCreator incstr;
		StringCreator libstr;
		StringCreator endstr;
		const char* str = NULL;
		const char* end = NULL;
		StringCreator globalstr;
		const char* content = this->content.c_str();

		while (*content)
		{
			if ((str = strstr(content, "<%")) == NULL)
			{
				string msg = stdx::trim(content, "\r\n");
				
				if (msg.length() > 0)
				{
					out<<"\tout<<\""<<translate(msg)<<"\";\n";
				}

				break;
			}

			if (str > content)
			{
				string msg(content, str);

				msg = stdx::trim(msg, "\r\n");

				if (msg.length() > 0)
				{
					out<<"\tout<<\""<<translate(msg)<<"\";\n";
				}
			}

			str = SkipStartString(str + 2, " \r\n\t");

			if ((end = strstr(str, "%>")) == NULL) return false;

			if (*str == '=')
			{
				string temp(str + 1, end);

				out<<"\tout<<("<<stdx::trim(temp, " \r\n\t;")<<");\n";
			}
			else if (*str == '@')
			{
				string temp(str + 1, end);

				temp = stdx::trim(temp);

				if (temp.length() > 8)
				{
					if (memcmp(temp.c_str(), "path", 4) == 0)
					{
						ContentNode node;

						temp = stdx::trim(temp, "<>");
						temp = stdx::replace(temp, "\t", " ");

						node.setEndSpliter(" ");
						node.setKeySpliter("=");
						node.parse(temp);

						string path = node.getValue("path");
						string access = node.getValue("access");

						stdx::toupper(access);

						path = stdx::trim(path, " \r\n\t<>\'\"");
						access = stdx::trim(access, " \r\n\t<>\'\"");

						if (access == "PUBLIC" || access == "PROTECT" || access == "PRIVATE" || access == "DISABLE")
						{
							access = "CGI_" + access;
						}
						else
						{
							access = "CGI_PRIVATE";
						}

						if (makstr.empty())
						{
							endstr<<"\nHTTP_WEBAPP(" + name + ", " + access + ", \"" + path + "\")";
						}
						else
						{
							endstr<<"\nHTTP_WEBCGI(" + access + ", \"" + path + "\")";
						}
					}
					else if (memcmp(temp.c_str(), "include", 7) == 0)
					{
						const char* ptr = temp.c_str() + 7;

						while (isspace(*ptr)) ptr++;

						if (*ptr == '=')
						{
							hdrstr<<"#include <"<<stdx::trim(++ptr, " \r\n\t<>\'\"")<<">\n";
						}
					}
					else if (memcmp(temp.c_str(), "incpath", 7) == 0)
					{
						const char* ptr = temp.c_str() + 7;

						while (isspace(*ptr)) ptr++;

						if (*ptr == '=')
						{
							string tmp = stdx::trim(++ptr, " \r\n\t<>\'\"");

							if (tmp[0] == '$' || (tmp[0] == '-' && tmp[1] == 'I'))
							{
								incstr<<tmp<<" ";
							}
							else
							{
								incstr<<"-I"<<tmp<<" ";
							}
						}
					}
					else if (memcmp(temp.c_str(), "library", 7) == 0)
					{
						const char* ptr = temp.c_str() + 7;

						while (isspace(*ptr)) ptr++;

						if (*ptr == '=')
						{
							string tmp = stdx::trim(++ptr, " \r\n\t<>\'\"");

							if (tmp[0] == '$' || (tmp[0] == '-' && tmp[1] == 'L'))
							{
								libstr<<tmp<<" ";
							}
							else
							{
								libstr<<"-L"<<tmp<<" ";
							}
						}
					}
				}
			}
			else
			{
				if (*str == '!')
				{
					str = SkipStartString(str + 1, " \r\n\t");

					globalstr<<string(str, end);
				}
				else
				{
					out<<"\t"<<string(str, end);
				}
			}

			content = end + 2;
		}

		string mak = stdx::replace(makstr, "<TARGET_NAME>", name);
		string src = stdx::replace(srcstr, "<TARGET_NAME>", name);

		str = out.str();

		while (isspace(*str)) str++;

		if (mak.find("OPENSSL") == string::npos)
		{
			mak = stdx::replace(mak, "$(PLATFORMLINK)", "$(LIB_OPENSSL) $(PLATFORMLINK)");
			mak = stdx::replace(mak, "$(LIBRARY_PATH)", "$(LIBRARY_PATH) $(INC_OPENSSL)");
		}

		mak = stdx::replace(mak, "<INCPATH>", incstr.str());
		src = hdrstr.getContent() + stdx::replace(src, "<CGI_SOURCE>", str);

		size_t pos = src.find("<GLOBAL_SOURCE>");
		
		if (pos != string::npos)
		{
			src = src.substr(0, pos) + globalstr.getContent() + src.substr(pos + strlen("<GLOBAL_SOURCE>"));
		}

		mak = stdx::replace(mak, "<LIBRARY>", libstr.str());
		src += endstr;

		File file;
		string path;
		string filepath;
		SmartBuffer buffer;
		const char* outpath = Process::GetCmdParam("-o");
	
		if (outpath && *outpath)
		{
			path = stdx::replace(outpath, "\\", "/") + "/";
		}
		else
		{
			path = cgipath + "src/";
		}
		
		if (makstr.length() > 0) path += name + "/";

		CHECK_FALSE_RETURN(path::mkdir(path));
		
		filepath = path + name + ".cpp";

		if (path::size(filepath) == src.length()) stdx::GetFileContent(buffer, filepath);

		if (buffer.isNull() || strncmp(buffer.str(), src.c_str(), buffer.size()))
		{
			CHECK_FALSE_RETURN(file.open(filepath, "wb+"));
			CHECK_FALSE_RETURN(file.write(src.c_str(), src.length()) > 0);
			file.flush();

			if (makstr.length() > 0)
			{
				filepath = path + "makefile";
				CHECK_FALSE_RETURN(file.open(filepath, "wb+"));
				CHECK_FALSE_RETURN(file.write(mak.c_str(), mak.length()) > 0);
				file.flush();
			}
		}

		return true;
	}
};


class HttpCGIEntityApplication : public Application
{
public:
	bool main()
	{
		if (GetCmdParam("-etc"))
		{
			puts("--- source template -----------------------------------------");
			puts("-------------------------------------------------------------\n");
			puts(GetSourceTemplate());
			puts("");
			puts("--- makefile template ---------------------------------------");
			puts("-------------------------------------------------------------\n");
			puts(GetMakefileTemplate());

			return true;
		}

		HttpCGIEntity entity;
		const char* path = GetCmdParam("-s");
		const char* cgipath = GetCmdParam("-t");

		if (path == NULL)
		{
			path = "./cgi";
			
			if (path::type(path) <= eNONE) return true;
		}

		if (cgipath == NULL) cgipath = "./";

		string str;
		string name;
		string parent;
		string makstr;
		string makallstr;
		vector<string> vec;
		string filepath = stdx::replace(cgipath, "\\", "/");

		if (GetCmdParam("-c") == NULL)
		{
			str = stdx::replace(cgipath, "\\", "/");

			if (str.back() == '/')
			{
				str += "cgi.mak";
			}
			else
			{
				str += "/cgi.mak";
			}

			entity.init(str, cgipath);
		
			if (filepath.back() == '/')
			{
				filepath += "makefile";
			}
			else
			{
				filepath += "/makefile";
			}
			
			if (stdx::GetFileContent(makstr, filepath) <= 0)
			{
				makallstr = "all:";
				makstr = "target: all\n\n";
				makstr += "MAKE = make\n\n";
			}
			else
			{
				size_t pos = makstr.find("all:");

				if (pos == string::npos)
				{
					makallstr = "all:";
				}
				else
				{
					makallstr = stdx::trim(makstr.substr(pos));
					makstr = makstr.substr(0, pos);
				}
			}
		}

		if (path::type(path) == ePATH)
		{
			if (stdx::GetFolderContent(vec, path, eFILE) <= 0) return true;

			parent = path;
		}
		else
		{
			name = path::name(path);
			parent = path::parent(path);
			vec.push_back(name);
		}
		
		
		parent = stdx::replace(parent, "\\", "/");

		if (parent.back() != '/')
		{
			parent += "/";
		}

		int idx = 1;
		size_t pos = 0;

		for (string& name : vec)
		{
			pos = name.rfind('.');

			if (pos == string::npos) continue;

			if (name.substr(pos + 1) != "cgi") continue;

			str = parent + name;
			name = name.substr(0, pos);
	
			SetConsoleTextColor(eYELLOW);
			printf(" %d.", idx++);
			SetConsoleTextColor(eWHITE);
			printf("entity : %s\n", name.c_str());
			puts("----------------------------");

			if (entity.init(str, cgipath))
			{
				SetConsoleTextColor(eGREEN);
				puts(" create entity success");
				cgipath = "";
			}
			else
			{
				SetConsoleTextColor(eRED);
				puts(" create entity failed");
				SetConsoleTextColor(eWHITE);

				return false;
			}

			if (entity.create())
			{
				puts(" export source success\n");
			}
			else
			{
				SetConsoleTextColor(eRED);
				puts(" export source failed");
				SetConsoleTextColor(eWHITE);

				return false;
			}

			if (makstr.length() > 0)
			{
				str = name + ":";

				if (makstr.find("\n" + str) == string::npos)
				{
					makstr += str + "\n";
					makstr += "\t$(MAKE) -C src/";
					makstr += name + "\n\n";
					makallstr += " " + name;
				}
			}
		}
	
		if (makstr.length() > 0)
		{
			File file;
			
			if (makallstr.length() > 4)
			{
				if (makstr[makstr.length() -1] == '\n')
				{
					makstr += makallstr;
				}
				else
				{
					makstr += "\n" + makallstr;
				}
			}

			if (!file.open(filepath, "wb+") || file.write(makstr.c_str(), makstr.length()) <= 0)
			{
				SetConsoleTextColor(eRED);
				puts(">>> update makefile failed");
			}
			else
			{
				SetConsoleTextColor(eGREEN);
				puts(">>> update makefile success");
				file.flush();
			}
		}

		SetConsoleTextColor(eWHITE);

		return true;
	}
};

START_APP(HttpCGIEntityApplication)